﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mani.Common;
using Gasco.DataAccess;
using Gasco.Business.Services;

namespace Gasco.Business.Workflows
{

    class DDPWorkflow : IOrderWorkflow
    {

        private StateManagerBase stateManager;
        private order order;

        public order Order
        {
            get { return this.order; }
        }

        private void InitializeStateManager()
        {
            State stInitial = stateManager.CreateState((int)OrderStates.Initial, false);
            State stWaitingForPayment = stateManager.CreateState((int)OrderStates.WaitingForPayment, false);
            State stWaitingForLoading = stateManager.CreateState((int)OrderStates.WaitingForLoading, false);
            State stWaitingForPriceCalc = stateManager.CreateState((int)OrderStates.WaitingForPriceCalc, false, new OnStateEnter(CheckPriceCalc));
            State stCancel = stateManager.CreateState((int)OrderStates.Cancel, false);
            State stComplete = stateManager.CreateState((int)OrderStates.Complete, false);

            stInitial.AddNewTransition((int)OrderEvent.ApproveOrder, "ApproveOrder", stWaitingForLoading, new TransitionGuard(CheckPurchaseOrderDone), null);
            stInitial.AddNewTransition((int)OrderEvent.CancelOrder, "CancelOrder", stCancel, null, null);

            stWaitingForLoading.AddNewTransition((int)OrderEvent.OrderLoaded, "OrderLoaded", stWaitingForPayment, null, null);
            stWaitingForLoading.AddNewTransition((int)OrderEvent.CancelOrder, "CancelOrder", stCancel, new TransitionGuard(CheckNothingLoaded), null);

            stWaitingForPayment.AddNewTransition((int)OrderEvent.OrderPaid, "OrderPaid", stWaitingForPriceCalc, null, null);
            stWaitingForPayment.AddNewTransition((int)OrderEvent.CancelOrder, "CancelOrder", stCancel, null, null);

            stWaitingForPriceCalc.AddNewTransition((int)OrderEvent.PriceCalculated, "PriceCalculated", stComplete, new TransitionGuard(CheckPriceCalculated), null);

            stComplete.AddNewTransition((int)OrderEvent.ReturnToInitial, "ReturnToInitial", stInitial, null, null);

            stCancel.AddNewTransition((int)OrderEvent.UndoCancel, "UndoCancel", stInitial, null, null);
        }

        /// <summary>
        /// The constructor acts as the DB-loader + stateManager initializer
        /// </summary>
        /// <param name="row"></param>
        public DDPWorkflow(order o)
        {
            this.order = o;

            stateManager = new StateManagerBase();
            InitializeStateManager();
            stateManager.completedMethod += new OnTransitionCompleted(PersistOrderState);
            stateManager.SetStartingState(o.order_status_id);
        }

        /// <summary>
        /// Persistance Code
        /// </summary>
        protected void PersistOrderState()
        {
            this.order.update_date = DateTime.Now;
            this.order.order_status_id = this.stateManager.CurrentState.Id;
            DatabaseHelper.Update<order>(this.order);
        }

        public void SendEvent(OrderEvent eventId)
        {
            stateManager.Send((int)eventId);
        }

        public Dictionary<int, Transition> GetTransitionsByState(int stateId)
        {
            return this.stateManager.GetTransitionsByState(stateId);
        }

        #region State Entries

        private void CheckPriceCalc()
        {
            if (this.order.price_calculation_type_id == (int)PriceCalculationType.Fixed)
                this.SendEvent(OrderEvent.PriceCalculated);
        }

        #endregion

        #region Guards

        private bool CheckPurchaseOrderDone(out string err)
        {
            err = "";
            if (this.order.order_type == (int)OrderType.Purchase)
                return true;

            OrderService os = new OrderService();
            order purchaseOrder = os.GetOrderById(this.order.purchase_order.Value);
            if (purchaseOrder.order_status_id == (int)OrderStates.Complete)
                return true;

            err = "PurchaseOrderNotFinishedException";
            return false;
        }

        private bool CheckLoadingCompleted(out string err)
        {
            err = "";
            LoadingService ls = new LoadingService();
            IList<loading> loadings = ls.GetLoadingsByOrder(this.order.id);
            double sum = loadings.Sum(l => l.quantity);

            if (sum == this.order.quantity)
                return true;

            err = "LoadingNotCompletedException";
            return false;
        }

        private bool CheckNothingLoaded(out string err)
        {
            err = "";
            LoadingService ls = new LoadingService();
            IList<loading> loadings = ls.GetLoadingsByOrder(this.order.id);

            if (loadings.Count == 0)
                return true;

            err = "LoadingIsinProgressException";
            return false;
        }

        private bool CheckPriceCalculated(out string err)
        {
            err = "";
            if (this.order.price.HasValue)
                return true;

            err = "PriceNotCalculatedException";
            return false;
        }

        #endregion
    }
}
